object Demo09Func2 {
  /**
   *
   * 面向对象编程：将对象传来传去 对象可以作为函数的参数或者返回值传来传去 注意有类型的约束
   * 面向函数编程：将函数传来传去 函数可以作为函数的参数或者返回值传来传去 注意也有类型的约束
   *
   * 面向函数（函数式编程、高阶函数）
   * 1、函数作为参数
   * 2、函数作为返回值
   *
   *
   * 如何描述函数的类型？
   * 跟def无关
   * 跟方法名无关
   * 跟方法体无关
   * 跟参数的名称无关
   *
   * 函数的类型跟该函数 ”参数的类型、参数的个数、参数的顺序、返回值类型“ 有关
   * (参数, 参数) => 返回值类型
   *
   */

  // 这是一个 ”参数只有一个类型为String 返回值类型为Int“ 这样的函数
  // String => Int
  def func1(str: String): Int = {
    str.toInt + 100
  }

  // 这是一个 ”参数只有一个类型为String 返回值类型为Int“ 这样的函数
  // String => Int
  def func2(str: String): Int = {
    str.toInt + 200
  }

  // 这是一个 ”参数只有一个类型为Int 返回值类型为String“ 这样的函数
  // Int => String
  def func3(int: Int): String = {
    int + 300 + ""
  }

  // 可以接收一个函数并进行调用
  // 接收一个 “String=>Int” 这样类型的函数
  def funcX(func: String => Int): Unit = {
    println(func("500"))
  }

  /**
   * 匿名函数（类似lambda表达式）：没有名字的函数
   *  (参数名:参数类型,参数名:参数类型,参数名:参数类型) => {方法体}
   */

  (str: String) => {
    str.toInt + 300
  }
  // 匿名函数也可以拥有名称
  val func4: String => Int = (str: String) => {
    str.toInt + 400
  }
  val func5: String => Int = (str) => {
    str.toInt + 500
  }

  def main(args: Array[String]): Unit = {
    funcX(func1)
    funcX(_.toInt + 100) // 用匿名函数替代func1的逻辑
    funcX(func2)
    funcX(_.toInt + 200) // 用匿名函数替代func2的逻辑
    //    funcX(func3) // func3不符合funcX的定义

    funcX(func4)
    funcX(func5)

    /**
     * 匿名函数的简化：
     * 1、如果代码只有一行 花括号可以省略
     * 2、如果匿名函数最为 参数 传给另外一个函数 则匿名函数的参数类型可以省略
     * 3、如果匿名函数只有一个参数则括号可以省略
     * 4、如果匿名函数中的参数在逻辑中只使用了一次 则可以用下划线替代 然后 => 左边的部分可以省略
     */
    funcX((str: String) => {
      str.toInt + 300
    }) // 太繁琐 很多东西可以省略
    funcX((str: String) => str.toInt + 300)
    funcX((str) => str.toInt + 300)
    funcX(str2 => str2.toInt + 300)
    funcX(_.toInt + 300) // 可读性比较低
  }

}
